// 文件路径: dbms/execution/execution/IndexScanExecutor.java
package com.dbms.execution.execution;

import com.dbms.catalog.Catalog;
import com.dbms.common.RID;
import com.dbms.common.Schema;
import com.dbms.common.Tuple;
import com.dbms.execution.plan.PlanNode;
import com.dbms.storage.BPlusTree;
import com.dbms.storage.BufferPoolManager;
import com.dbms.storage.Page;
import com.dbms.storage.TablePage;

import java.io.IOException;

/**
 * IndexScanExecutor (索引扫描执行器) 负责通过索引来查找单个元组。
 * 它的工作流程是：
 * 1. 在 `open()` 方法中，使用给定的键（key）在 B+ 树索引中查找对应的记录标识符 (RID)。
 * 2. 在 `next()` 方法中，使用这个 RID 从缓冲池中获取数据页，并从页面中抽取出元组数据。
 * 这种方式比全表扫描 (SeqScan) 在查找特定键时要高效得多。
 */
public class IndexScanExecutor implements Executor {

    private final BufferPoolManager bufferPoolManager;
    private final BPlusTree index;
    private final Schema schema;
    private final Integer key; // 要查找的主键
    private RID resultRid; // 索引查找的结果
    private boolean hasExecuted = false; // 确保 next() 只执行一次

    // 【修改】用于 EXPLAIN ANALYZE 的性能指标变量
    private long startTimeNanos;
    private long totalTimeNanos;
    private long rowsProduced;
    private final String tableName;

    public IndexScanExecutor(BufferPoolManager bpm, Catalog.TableMetadata tableInfo, Integer key) {
        this.bufferPoolManager = bpm;
        this.index = tableInfo.index;
        this.schema = tableInfo.schema;
        this.key = key;
        this.tableName = tableInfo.tableName;
    }

    @Override
    public void open() {
        this.rowsProduced = 0;
        // 【修改】使用 System.nanoTime() 记录开始时间
        this.startTimeNanos = System.nanoTime();
        try {
            // 在 open 阶段就执行索引查找，获取 RID
            this.resultRid = index.find(key);
        } catch (IOException e) {
            e.printStackTrace();
            this.resultRid = null;
        }
        this.hasExecuted = false;
    }

    @Override
    public Tuple next() {
        // 如果已经执行过，或者在 open() 中没有找到 RID，则直接返回 null
        if (hasExecuted || resultRid == null) {
            return null;
        }
        Page page = null;
        try {
            // 1. 使用 RID 从缓冲池中获取数据页
            page = bufferPoolManager.fetchPage(resultRid.getPageId());
            page.rLock(); // 加读锁，因为我们只读取数据
            TablePage tablePage = new TablePage(page);
            // 2. 从页面中根据槽位号获取元组的字节数据
            byte[] tupleData = tablePage.getTuple(resultRid.getSlotNum());
            if (tupleData == null) {
                return null; // 可能元组已被删除
            }
            this.rowsProduced++;
            // 3. 将字节数据反序列化为 Tuple 对象并返回
            return new Tuple(tupleData, schema);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            if (page != null) {
                page.rUnlock(); // 释放读锁
                bufferPoolManager.unpinPage(page.getPageId(), false); // 解钉页面，false表示页面未被修改
                hasExecuted = true; // 标记为已执行，因为索引扫描只返回一个结果
            }
        }
    }

    @Override
    public void close() {
        // 【修改】在 close 阶段计算总耗时
        this.totalTimeNanos = System.nanoTime() - startTimeNanos;
    }

    @Override
    public Schema getSchema() {
        return schema;
    }

    @Override
    public PlanNode getPlanNode() {
        // 为 EXPLAIN ANALYZE 创建计划节点
        String details = String.format("on table '%s' with key %d", tableName, key);
        PlanNode node = new PlanNode("Index Scan", details);
        // 【修改】设置从性能指标中收集到的执行时间和产出行数
        node.setExecutionTimeNanos(this.totalTimeNanos);
        node.setRowsProduced(this.rowsProduced);
        return node;
    }
}